package com.apes.framework.jpa;


import com.apes.framework.jpa.comment.CommentService;
import com.apes.framework.util.JpaUtil;
import com.apes.framework.util.ReflectUtil;
import lombok.Data;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedCaseInsensitiveMap;

import javax.persistence.Column;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 功能：Jpa模型元数据
 *
 * @author xul
 * @create 2018-06-08 9:22
 */
@Service
@Order
public class TableMetadata implements CommandLineRunner {

    private static Map<String, List<Field>> metadata = new LinkedCaseInsensitiveMap();

    @Override
    public void run(String... args) throws Exception {
        JpaUtil.getAllDomainType().forEach(domainTypeName -> {
            Class domainType = ReflectUtil.getClass(domainTypeName);
            create(domainType);
        });
    }

    public void create(Class entityClazz) {
        Table table = ReflectUtil.getAnnotation(entityClazz, Table.class);
        if (table == null) return;
        PropertyDescriptor[] descriptors = ReflectUtil.getPropertyDescriptors(entityClazz);
        if (descriptors == null) return;
        List fields = new ArrayList();
        for (PropertyDescriptor descriptor : descriptors) {
            Class fieldClass = descriptor.getPropertyType();
            String description = CommentService.getColumnDescription(entityClazz, descriptor);
            if (ReflectUtil.isPrimitive(fieldClass) || Date.class.isAssignableFrom(fieldClass)) {
                Column column = ReflectUtil.getAnnotation(entityClazz, descriptor.getName(), Column.class);
                if (column == null)
                    column = ReflectUtil.getAnnotation(BaseModel.class, descriptor.getName(), Column.class);
                if (column == null) column = ReflectUtil.getAnnotation(Model.class, descriptor.getName(), Column.class);
                if (column != null) {
                    fields.add(new Field(descriptor.getName(), column.name(), fieldClass.getName(), column.length(), description));
                }
            } else if (ApesBean.class.isAssignableFrom(fieldClass)) {
                ManyToOne mto = ReflectUtil.getAnnotation(entityClazz, descriptor.getName(), ManyToOne.class);
                JoinColumn jc = ReflectUtil.getAnnotation(entityClazz, descriptor.getName(), JoinColumn.class);
                if (mto != null && jc != null) {
                    PropertyDescriptor pd = ReflectUtil.getFieldPropertyDescriptor(fieldClass, jc.referencedColumnName());
                    Column column = null;
                    if (pd != null) {
                        column = ReflectUtil.getAnnotation(fieldClass, pd.getName(), Column.class);
                    }
                    fields.add(new Field(descriptor.getName(), jc.name(), fieldClass.getName(), column != null ? column.length() : 20, description));
                }
            }
        }
        metadata.put(table.name(), fields);
    }

    public static Field getFieldByName(String table, String name) {
        try {
            return metadata.get(table).stream().filter(field -> field.name.equalsIgnoreCase(name)).findFirst().get();
        } catch (Exception e) {
            return null;
        }
    }

    public static Field getFieldByField(String table, String fieldName) {
        try {
            return metadata.get(table).stream().filter(field -> field.field.equalsIgnoreCase(fieldName)).findFirst().get();
        } catch (Exception e) {
            return null;
        }
    }

    @Data
    public static class Field {
        private String name;
        private String field;
        private String type;
        private int length;
        private String comment;

        public Field(String name, String field, String type, int length, String comment) {
            this.name = name;
            this.field = field;
            this.type = type;
            this.length = length;
            this.comment = comment;
        }
    }
}
